using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authorization;
using AutoMapper;
using AiPlatformAdmin.Api.Data;
using AiPlatformAdmin.Api.Models;
using AiPlatformAdmin.Api.Services;
using AiPlatformAdmin.Api.DTOs;

namespace AiPlatformAdmin.Api.Controllers;

[ApiController]
[Route("api/agents")]
[Authorize]
public class AgentsController : ControllerBase
{
    private readonly AppDbContext _context;
    private readonly ITenantService _tenantService;
    private readonly IMapper _mapper;

    public AgentsController(AppDbContext context, ITenantService tenantService, IMapper mapper)
    {
        _context = context;
        _tenantService = tenantService;
        _mapper = mapper;
    }

    private int GetCurrentUserId()
    {
        var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return int.TryParse(userIdClaim, out int userId) ? userId : 0;
    }

    private async Task<bool> CanUserAccessAgent(int agentId, int userId)
    {
        var agent = await _context.Agents.FindAsync(agentId);
        if (agent == null) return false;

        if (User.IsInRole("Administrator")) return true;
        if (agent.IsPublic) return true;

        if (await _context.AgentAdmins.AnyAsync(aa => aa.AgentId == agentId && aa.UserId == userId))
            return true;

        var userTeamIds = await _context.UserTeams
            .Where(ut => ut.UserId == userId)
            .Select(ut => ut.TeamId)
            .ToListAsync();

        return await _context.AgentTeamAccesses
            .AnyAsync(ata => ata.AgentId == agentId && userTeamIds.Contains(ata.TeamId));
    }

    private async Task<bool> IsUserAgentAdmin(int agentId, int userId)
    {
        if (User.IsInRole("Administrator")) return true;
        return await _context.AgentAdmins.AnyAsync(aa => aa.AgentId == agentId && aa.UserId == userId);
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<AgentDto>>> GetAgents()
    {
        var userId = GetCurrentUserId();
        List<Agent> agents;

        if (User.IsInRole("Administrator"))
        {
            agents = await _context.Agents
                .Include(a => a.AgentKnowledgeBases)
                .Include(a => a.AgentPlugins)
                .Include(a => a.AgentAdmins)
                .Include(a => a.AgentTeamAccesses)
                .ToListAsync();
        }
        else
        {
            var userTeamIds = await _context.UserTeams
                .Where(ut => ut.UserId == userId)
                .Select(ut => ut.TeamId)
                .ToListAsync();

            var adminAgentIds = await _context.AgentAdmins
                .Where(aa => aa.UserId == userId)
                .Select(aa => aa.AgentId)
                .ToListAsync();

            var teamAccessAgentIds = await _context.AgentTeamAccesses
                .Where(ata => userTeamIds.Contains(ata.TeamId))
                .Select(ata => ata.AgentId)
                .ToListAsync();

            var accessibleAgentIds = adminAgentIds.Union(teamAccessAgentIds).ToList();

            agents = await _context.Agents
                .Include(a => a.AgentKnowledgeBases)
                .Include(a => a.AgentPlugins)
                .Include(a => a.AgentAdmins)
                .Include(a => a.AgentTeamAccesses)
                .Where(a => a.IsPublic || accessibleAgentIds.Contains(a.Id))
                .ToListAsync();
        }

        return Ok(_mapper.Map<IEnumerable<AgentDto>>(agents));
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<AgentDto>> GetAgent(int id)
    {
        var userId = GetCurrentUserId();
        if (!await CanUserAccessAgent(id, userId))
        {
            return Forbid();
        }

        var agent = await _context.Agents
            .Include(a => a.AgentKnowledgeBases)
            .Include(a => a.AgentPlugins)
            .Include(a => a.AgentAdmins)
            .Include(a => a.AgentTeamAccesses)
            .FirstOrDefaultAsync(a => a.Id == id);

        if (agent == null)
        {
            return NotFound();
        }

        return Ok(_mapper.Map<AgentDto>(agent));
    }

    [HttpPost]
    [Authorize(Roles = "PlatformUser,Administrator")]
    public async Task<ActionResult<AgentDto>> CreateAgent(CreateAgentDto dto)
    {
        if (!_tenantService.CurrentTenantId.HasValue)
        {
            return Forbid();
        }

        var userId = GetCurrentUserId();
        var agent = _mapper.Map<Agent>(dto);

        _context.Agents.Add(agent);
        await _context.SaveChangesAsync();

        // Make the creator an admin
        if (!User.IsInRole("Administrator"))
        {
            _context.AgentAdmins.Add(new AgentAdmin
            {
                AgentId = agent.Id,
                UserId = userId
            });
        }

        // Add admin users
        if (dto.AdminUserIds != null && dto.AdminUserIds.Any())
        {
            foreach (var adminUserId in dto.AdminUserIds)
            {
                if (!await _context.AgentAdmins.AnyAsync(aa => aa.AgentId == agent.Id && aa.UserId == adminUserId))
                {
                    _context.AgentAdmins.Add(new AgentAdmin { AgentId = agent.Id, UserId = adminUserId });
                }
            }
        }

        // Add team accesses
        if (dto.TeamAccessIds != null && dto.TeamAccessIds.Any())
        {
            foreach (var teamId in dto.TeamAccessIds)
            {
                _context.AgentTeamAccesses.Add(new AgentTeamAccess { AgentId = agent.Id, TeamId = teamId });
            }
        }

        // Add knowledge bases
        if (dto.KnowledgeBaseIds != null && dto.KnowledgeBaseIds.Any())
        {
            foreach (var kbId in dto.KnowledgeBaseIds)
            {
                _context.AgentKnowledgeBases.Add(new AgentKnowledgeBase { AgentId = agent.Id, KnowledgeBaseId = kbId });
            }
        }

        // Add plugins
        if (dto.PluginIds != null && dto.PluginIds.Any())
        {
            foreach (var pluginId in dto.PluginIds)
            {
                _context.AgentPlugins.Add(new AgentPlugin { AgentId = agent.Id, PluginId = pluginId });
            }
        }

        await _context.SaveChangesAsync();

        // Reload with all relationships
        await _context.Entry(agent).Collection(a => a.AgentAdmins).LoadAsync();
        await _context.Entry(agent).Collection(a => a.AgentTeamAccesses).LoadAsync();
        await _context.Entry(agent).Collection(a => a.AgentKnowledgeBases).LoadAsync();
        await _context.Entry(agent).Collection(a => a.AgentPlugins).LoadAsync();

        return CreatedAtAction(nameof(GetAgent), new { id = agent.Id }, _mapper.Map<AgentDto>(agent));
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateAgent(int id, UpdateAgentDto dto)
    {
        var userId = GetCurrentUserId();
        if (!await IsUserAgentAdmin(id, userId))
        {
            return Forbid();
        }

        var agent = await _context.Agents
            .Include(a => a.AgentAdmins)
            .Include(a => a.AgentTeamAccesses)
            .Include(a => a.AgentKnowledgeBases)
            .Include(a => a.AgentPlugins)
            .FirstOrDefaultAsync(a => a.Id == id);

        if (agent == null)
        {
            return NotFound();
        }

        _mapper.Map(dto, agent);

        // Update admins if provided
        if (dto.AdminUserIds != null)
        {
            var existingAdmins = agent.AgentAdmins.Select(aa => aa.UserId).ToList();
            var newAdmins = dto.AdminUserIds.ToList();

            var toAdd = newAdmins.Except(existingAdmins);
            var toRemove = existingAdmins.Except(newAdmins);

            foreach (var adminUserId in toAdd)
            {
                _context.AgentAdmins.Add(new AgentAdmin { AgentId = id, UserId = adminUserId });
            }

            foreach (var adminUserId in toRemove)
            {
                var adminToRemove = agent.AgentAdmins.FirstOrDefault(aa => aa.UserId == adminUserId);
                if (adminToRemove != null)
                {
                    _context.AgentAdmins.Remove(adminToRemove);
                }
            }
        }

        // Update team accesses if provided
        if (dto.TeamAccessIds != null)
        {
            var existingTeams = agent.AgentTeamAccesses.Select(ata => ata.TeamId).ToList();
            var newTeams = dto.TeamAccessIds.ToList();

            var toAdd = newTeams.Except(existingTeams);
            var toRemove = existingTeams.Except(newTeams);

            foreach (var teamId in toAdd)
            {
                _context.AgentTeamAccesses.Add(new AgentTeamAccess { AgentId = id, TeamId = teamId });
            }

            foreach (var teamId in toRemove)
            {
                var teamAccessToRemove = agent.AgentTeamAccesses.FirstOrDefault(ata => ata.TeamId == teamId);
                if (teamAccessToRemove != null)
                {
                    _context.AgentTeamAccesses.Remove(teamAccessToRemove);
                }
            }
        }

        // Update knowledge bases if provided
        if (dto.KnowledgeBaseIds != null)
        {
            var existingKbs = agent.AgentKnowledgeBases.Select(akb => akb.KnowledgeBaseId).ToList();
            var newKbs = dto.KnowledgeBaseIds.ToList();

            var toAdd = newKbs.Except(existingKbs);
            var toRemove = existingKbs.Except(newKbs);

            foreach (var kbId in toAdd)
            {
                _context.AgentKnowledgeBases.Add(new AgentKnowledgeBase { AgentId = id, KnowledgeBaseId = kbId });
            }

            foreach (var kbId in toRemove)
            {
                var kbToRemove = agent.AgentKnowledgeBases.FirstOrDefault(akb => akb.KnowledgeBaseId == kbId);
                if (kbToRemove != null)
                {
                    _context.AgentKnowledgeBases.Remove(kbToRemove);
                }
            }
        }

        // Update plugins if provided
        if (dto.PluginIds != null)
        {
            var existingPlugins = agent.AgentPlugins.Select(ap => ap.PluginId).ToList();
            var newPlugins = dto.PluginIds.ToList();

            var toAdd = newPlugins.Except(existingPlugins);
            var toRemove = existingPlugins.Except(newPlugins);

            foreach (var pluginId in toAdd)
            {
                _context.AgentPlugins.Add(new AgentPlugin { AgentId = id, PluginId = pluginId });
            }

            foreach (var pluginId in toRemove)
            {
                var pluginToRemove = agent.AgentPlugins.FirstOrDefault(ap => ap.PluginId == pluginId);
                if (pluginToRemove != null)
                {
                    _context.AgentPlugins.Remove(pluginToRemove);
                }
            }
        }

        await _context.SaveChangesAsync();
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteAgent(int id)
    {
        var userId = GetCurrentUserId();
        if (!await IsUserAgentAdmin(id, userId))
        {
            return Forbid();
        }

        var agent = await _context.Agents.FindAsync(id);
        if (agent == null)
        {
            return NotFound();
        }

        _context.Agents.Remove(agent);
        await _context.SaveChangesAsync();

        return NoContent();
    }
}
